package com.minelx.sequence.pattern;

import com.minelx.sequence.core.Match;
import com.minelx.sequence.core.ReturnMatcher;
import com.minelx.sequence.core.StringScanner;
import com.minelx.sequence.core.ThenMatcher;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.util.stream.Collectors.toList;

public class PatternParser implements ReturnMatcher<ReturnMatcher<?, StringScanner>, StringScanner> {
	private final ReturnMatcher<ReturnMatcher<?, StringScanner>, StringScanner> unitMatchers;

	private PatternParser(IVariablePatternParserFactory variablePatternParserFactory,
	                      Map<String, ReturnMatcher<?, StringScanner>> configuration) {
		ReturnMatcher<ReturnMatcher<?, StringScanner>, StringScanner> unitMatcher = unitMatcher(configuration);
		unitMatchers = variablePatternParserFactory.createVariablePatternParser(this)
				.or(new EnumeratePatternMatcher(unitMatcher))
				.or(unitMatcher)
		;
	}

	@Override
	public boolean isMatch(StringScanner clue) {
		return unitMatchers.isMatch(clue);
	}

	@Override
	public ReturnMatcher<?, StringScanner> onMatched(StringScanner clue) {
		List<ReturnMatcher<?, StringScanner>> actualTextMatchers = clue.stream(unitMatchers)
				.map(unit -> new RepeatPatternMatcher(unit).get(clue).orElse(unit))
				.map(unit -> new JoinPatternMatcher(unit).get(clue).orElse(unit))
				.map(unit -> new StarMatcher(unit).get(clue).orElse(unit))
				.collect(toList());
		return ThenMatcher.of(actualTextMatchers);
	}

	public static ReturnMatcher<ReturnMatcher<?, StringScanner>, StringScanner> unitMatcher(Map<String, ReturnMatcher<?, StringScanner>> configuration) {
		configuration.put("ALPHA", Match.anAlpha());
		configuration.put("DIGIT", Match.aDigit());
		return new ConfigurableMatcher(configuration).or(new SingleCharMatcher());
	}

	public static PatternParser create(IVariablePatternParserFactory variablePatternParserFactory) {
		return create(variablePatternParserFactory, new HashMap<>());
	}

	public static PatternParser create(IVariablePatternParserFactory variablePatternParserFactory,
	                                   Map<String, ReturnMatcher<?, StringScanner>> variantMatcherConfiguration) {
		return new PatternParser(variablePatternParserFactory, variantMatcherConfiguration);
	}

	// FIXME 2021/7/25  wait for me!!!     Builder
	public static PatternParser create(IVariablePatternParserFactory variablePatternParserFactory,
	                                   String configAsText) {
		Map<String, ReturnMatcher<?, StringScanner>> configurations = new HashMap<>();
		PatternParser result = create(variablePatternParserFactory, configurations);
		Arrays.stream(configAsText.split("\n"))
				.map(StringScanner::source)
				.forEach(source -> configurations.put(source.til(':'), result.get(source).orElseThrow(RuntimeException::new)));
		return result;
	}
}
